const db eps = 1e-8;
int sign(db x) {return (x > eps) - (x < -eps);}

struct P {
	db x, y, z;
	P() {x = y = z = 0;}
	P(db x, db y, db z) {this->x = x; this->y = y; this->z = z;}
	void read() {scanf("%lf%lf%lf", &x, &y, &z);}
	P operator + (const P& c) const {return P(x + c.x, y + c.y, z + c.z);}
	P operator - (const P& c) const {return P(x - c.x, y - c.y, z - c.z);}
	P operator * (const db& c) const {return P(x * c, y * c, z * c);}
	P operator / (const db& c) const {return P(x / c, y / c, z / c);}
	bool operator < (const P& c) const {
		if (sign(x - c.x)) return sign(x - c.x) < 0;
		if (sign(y - c.y)) return sign(y - c.y) < 0;
		return sign(z - c.z) < 0;
	}
	bool operator == (const P& c) const {
		return !sign(x - c.x) && !sign(y - c.y) && !sign(z - c.z);
	}
	bool operator != (const P& c) const {return !(*this == c);}
};

db dot(P a, P b) {return a.x * b.x + a.y * b.y + a.z * b.z;}
P cross(P a, P b) {return P(a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x);}
db abs(P a) {return sqrt(dot(a, a));}
db norm(P a) {return dot(a,a);}
db ang(P a, P b) {return acos(dot(a, b) / abs(a) / abs(b));}
P reflect(P v, P fa) {return v - fa / abs(fa) * dot(v, fa) / abs(fa) * 2;}
db mix(P a, P b, P c) {return dot(a, cross(b, c));}
db volume(P a, P b, P c, P d) {return mix(b - a, c - a, d - a);}
db area(P a, P b, P c) {return abs(cross(b - a, c - a));}

bool isPL(P a, P b, P c, P l0, P l1, P &ans) {//线面交点
	P p = cross(b - a, c - a);
	if (sign(dot(l1 - l0, p)) == 0) return false;
	db t = (p.x * (a.x - l0.x) + p.y * (a.y - l0.y) + p.z * (a.z - l0.z)) / (p.x * (l1.x - l0.x) + p.y * (l1.y - l0.y) + p.z * (l1.z - l0.z));
	ans = l0 + (l1 - l0) * t;
	return true;
}

bool ons(P a, P b, P o) {
	if (a == o || b == o) return true;
	return sign(abs(cross(a - o, b - o))) == 0 && sign(dot(a - o, b - o)) < 0;
}

db disLL(P p1, P p11, P p2, P p22) {//线线距离
	P u = p11 - p1, v = p22 - p2, f = cross(u, v);
	return fabs(dot(f, p2 - p1) / abs(f));
}

vector<P> isLC(P s, P b, C c) {//线圆交点
	vector<P> ans; P v = b - s;
	db A = dot(v, v), B = 2 * dot(s - c.o, v),
	   C = dot(s - c.o, s - c.o) - c.r * c.r;
	db delta = (B * B - 4 * A * C);
	if (sign(delta) >= 0) {
		if (delta < 0) delta = 0; db d = sqrt(delta);
		db t1 = (-B + d) / 2 / A, t2 = (-B - d) / 2 / A;
		if (sign(t1) > 0) ans.pb(s + v * t1);
		if (sign(t2) > 0) ans.pb(s + v * t2);
	}
	return ans;
}

/*3D凸包
P info[1005];
int mark[1005][1005], n, cnt;
db mix(int a, int b, int c) {return mix(info[a], info[b], info[c]);}
db volume(int a, int b, int c, int d) {return volume(info[a], info[b], info[c], info[d]);}
db area(int a, int b, int c) {return area(info[a], info[b], info[c]);}

struct Face {
    int a, b, c; Face() {}
    Face(int a, int b, int c): a(a), b(b), c(c) {}
    int &operator [](int k) { 
        if (k == 0) return a; if (k == 1) return b; return c;
    }
};
vector <Face> face;
inline void insert(int a, int b, int c) {face.push_back(Face(a, b, c));}
void add(int v) {
    vector <Face> tmp; int a, b, c; cnt++;
    for (int i = 0; i < sz(face); i++) {
        a = face[i][0]; b = face[i][1]; c = face[i][2];
        if (sign(volume(v, a, b, c)) < 0)
            mark[a][b] = mark[b][a] = mark[b][c] = mark[c][b] = mark[c][a] = mark[a][c] = cnt;
        else tmp.push_back(face[i]);
    } face = tmp;
    for (int i = 0; i < sz(tmp); i++) {
        a = face[i][0]; b = face[i][1]; c = face[i][2];
        if (mark[a][b] == cnt) insert(b, a, v);
        if (mark[b][c] == cnt) insert(c, b, v);
        if (mark[c][a] == cnt) insert(a, c, v);
    }
}

int Find() {
    for (int i = 2; i < n; i++) {
        P ndir = cross(info[0] - info[i], info[1] - info[i]);
        if (ndir == P()) continue; swap(info[i], info[2]);
        for (int j = i + 1; j < n; j++) if (sign(volume(0, 1, 2, j)) != 0) {
            swap(info[j], info[3]); insert(0, 1, 2); insert(0, 2, 1); return 1;
        } 
    }
    return 0; 
}

double calcDist(const P &p, int a, int b, int c) { // 求重心
    return fabs(mix(info[a] - p, info[b] - p, info[c] - p) / area(a, b, c));
}
double findDist() { //3D凸包重心
    double totalWeight = 0; 
    P center(0.0, 0.0, 0.0);
    P first = info[face[0][0]];
    for (int i = 0; i < face.size(); ++i) {
        P p = (info[face[i][0]] + info[face[i][1]] + info[face[i][2]] + first) * 0.25;
        double weight = mix(info[face[i][0]] - first, info[face[i][1]] - first, info[face[i][2]] - first); 
        totalWeight += weight; center = center + p * weight;
    } 
    center = center / totalWeight;
    double res = 1e100; //compute distance 
    for (int i = 0; i < face.size(); ++i)
        res = min(res, calcDist(center, face[i][0], face[i][1], face[i][2]));
    return res; 
}

void convexhull3D() {
	scanf("%d", &n);
	for (int i = 1; i < n; i++) info[i].read();
	sort(info, info + n); n = unique(info, info + n) - info;
	face.clear(); random_shuffle(info, info + n);
	if (Find()) for (int i = 3; i < n; i++) add(i);
}*/
